package com.hksj.app.service;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hksj.app.dto.FileDTO;
import com.hksj.app.entity.FileEntity;
import com.hksj.app.mapper.FileMapper;
import com.hksj.app.utils.FileTypeUtils;
import com.hksj.common.core.model.Rest;
import org.apache.commons.io.IOUtils;
import org.apache.poi.openxml4j.opc.ContentTypes;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @see com.hksj.app.service.FileService
 * @author zengke
 * @version 1.0
 * @since 2023/9/8 16:21
 */

/**
 * 文件上传
 */
@Service
public class FileService extends ServiceImpl<FileMapper, FileEntity> {

    //文件上传路径根目录 /file
    public static final String linuxRootPath = File.separator + "file";

    //windows路径 C:\file
    public static final String windowsRootPath = "C:" + File.separator + "file";

    /**
     * 文件上传
     * @param dirPath  文件上传的文件夹路径
     * @param multipartFile 上传文件数组(多个文件或单个文件都支持)
     */

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public List<FileEntity> upload(String dirPath, MultipartFile[] multipartFile){
        //文件的根目录
        String rootPath = "";

        //判断是windows操作系统还是linux操作系统
        String os = System.getProperty("os.name");

        //操作系统类型、文件存储该操作系统类型、用于后续查询文件时拼接访问url、1=Linux、2=Windows
        int osType;

        if (os != null && os.toLowerCase().startsWith("windows")){
            rootPath = windowsRootPath;
            osType = 2;
        } else if (os != null && os.toLowerCase().startsWith("linux")) {
            rootPath = linuxRootPath;
            osType = 1;
        } else {
            //其他操作系统,无法传输文件
            return null;
        }

        //若传输的文件大小为空则提示传输的文件为空
        if (multipartFile.length == 0){
            throw new RuntimeException("文件为空");
        }

        //创建指定的文件路径
        File dirFile = new File(rootPath + dirPath);

        //判断该文件夹路径是否存在
        if (!dirFile.exists()){
            //不存在创建文件夹
            if (!dirFile.mkdirs()){
                //判断是否创建成功,可能文件路径不存在就直接返回异常
                throw new RuntimeException("文件夹创建失败");
            }
        }

        FileEntity entity = null;
        List<FileEntity> list = new ArrayList<>();
        int count = 0;

        //该文件路径已存在或不存在已创建
        //遍历前端传递过来的文件数组
        for (MultipartFile file : multipartFile) {
            //获取数组中file对应的完整文件名(带后缀的)
            String name = file.getOriginalFilename();

            long size = file.getSize();

            //获取到文件后缀名
            int dotIndex = name.lastIndexOf(".");
            String suffix = name.substring(dotIndex + 1);

            String fileName = name.substring(0,dotIndex);


            if (FileTypeUtils.isImage(name) || FileTypeUtils.isAudio(name) || FileTypeUtils.isDocument(name) || FileTypeUtils.isMedia(name)){
                try {
                    //将临时文件转存到指定的地址 dirPath+"/"+该完整文件名
                    file.transferTo(new File(rootPath + dirPath + File.separator + name));
                } catch (IOException e) {
                    //转存出现问题,抛出异常
                    throw new RuntimeException("上传文件失败");
                }
            } else {
                throw new RuntimeException("文件格式不正确");
            }

            entity = new FileEntity();
            entity.setName(fileName);
            entity.setPath(dirPath);
            entity.setType(suffix);
            entity.setSize(size);
            entity.setSeq(String.valueOf(count++));
            entity.setRootPath(rootPath);
            entity.setOsType(osType);
            list.add(entity);
        }

        //调用批量新增文件的接口
        if (!this.saveBatch(list)){
            throw new RuntimeException("新增文件失败");
        }

        return list;
    }

    /**
     * 删除文件
     * @return
     */
    public Rest deleteFile(String fileId){

        //通过文件id查询在文件表中是否存在
        FileEntity entity = baseMapper.selectById(fileId);
        if (Objects.isNull(entity)){
            Rest.fail("输入的文件id有误,或文件已删除");
        }

        //文件在文件表中存在
        //删除文件表中的文件记录
        if (!this.removeById(fileId)){
            //判断是否删除成功
            return Rest.fail("删除文件失败");
        }

        //删除服务器指定文件夹下的文件
        //文件路径
        String filePath = entity.getPath() + entity.getName() + "." + entity.getType();
        if (FileUtil.del(filePath)){
            return Rest.ok("删除文件成功");
        } else {

            return Rest.fail("删除文件失败");
        }

    }

    //通过文件id集合查询文件详情
    public List<FileEntity> queryFileDetail(FileDTO fileDTO) {
/*        //判断传值是否为空
        if (StrUtil.isEmpty(fileIds)){
            return null;
        }

        //不为空,将fileIds按照逗号拆分成数组
        String[] fileIdArray = fileIds.split(",");

        //数组为空字符串,直接返回查询结果为空
        if (ObjectUtils.isEmpty(fileIdArray)){
            return null;
        }*/

        //传来的文件id集合为空
        if (ObjectUtils.isEmpty(fileDTO.getFileIdList())){
            return null;
        }

        //构造查询条件
        LambdaQueryWrapper<FileEntity> queryWrapper = new LambdaQueryWrapper<FileEntity>()
                .in(FileEntity::getId, fileDTO.getFileIdList());

        //调用baseMapper的selectList查询文件详情
        return baseMapper.selectList(queryWrapper);
    }

    /**
     * 功能描述:下载文件格式为zip
     *
     * @author niexiang
     * @date 2024/04/10
     */
    public void downloadFileZip(String id, HttpServletResponse response) {
        //通过id获取对应文件绝对路径
        FileEntity entity = baseMapper.selectById(id);

        //判断文件是否存在
        if (Objects.isNull(entity)){
            throw new RuntimeException("文件不存在");
        }

        //文件名称
        String fileName = entity.getName()+"." + entity.getType();

        //凭借绝对路径
        String filePath = entity.getRootPath()+entity.getPath()+File.separator + entity.getName() + "." + entity.getType();
        //String filePath = "E:\\me\\floor9R10.glb";
        try {
            // 设置响应头，告诉浏览器返回的数据是一个压缩包
            String zipFileName = "output.zip";
            //zipFileName 为中文时需要转码 URLEncoder.encode(zipFileName,"UTF-8")
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(zipFileName,"UTF-8"));
            response.setContentType("application/zip");
            //response.setHeader("Content-Disposition", "attachment; filename=output.zip");

            // 创建 ZipOutputStream 对象，将数据写入到 ServletOutputStream
            ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream());

            //输入流，通过输入流读取文件内容
            FileInputStream fileInputStream = new FileInputStream(new File(filePath));

            //指定文件名称
            ZipEntry zipEntry = new ZipEntry(fileName);
            zipOut.putNextEntry(zipEntry);

            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = fileInputStream.read(bytes)) != -1){
                zipOut.write(bytes, 0, len);
            }

            // 关闭 ZipOutputStream 和输入流
            zipOut.closeEntry();
            zipOut.close();
            fileInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void getImage(String id, HttpServletResponse response) {
        FileEntity entity = baseMapper.selectById(id);

        //判断文件是否存在
        if (Objects.isNull(entity)){
            throw new RuntimeException("文件不存在");
        }

        //文件名称
        String fileName = entity.getName()+"." + entity.getType();

        //凭借绝对路径
        String filePath = entity.getRootPath()+entity.getPath()+File.separator + entity.getName() + "." + entity.getType();

        try {
//            response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(fileName ,"utf-8"));
            response.setHeader("content-disposition","inline");
            response.setContentType(ContentTypes.getContentTypeFromFileExtension(fileName));
            ServletOutputStream outputStream = response.getOutputStream(); //字节流
            FileInputStream fileInputStream = new FileInputStream(new File(filePath));
            //流数据交换，每次交换10k数据大小 （1024k = 1m）
            byte[] bytes = new byte[1024*10];
            int read;
            do {
                read = fileInputStream.read(bytes);
                outputStream.write(bytes,0,read);
            }while (-1 != read);
            //关闭资源
            IOUtils.closeQuietly(fileInputStream);
            IOUtils.closeQuietly(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 功能描述:下载问价格式为原本的格式
     *
     * @author niexiang
     * @date 2024/04/10
     */
    public void downloadFile(String id, HttpServletResponse response) {
        //通过id获取对应文件绝对路径
        FileEntity entity = baseMapper.selectById(id);

        //判断文件是否存在
        if (Objects.isNull(entity)){
            throw new RuntimeException("文件不存在");
        }

        //文件名称
        String fileName = entity.getName()+"." + entity.getType();

        //凭借绝对路径
        String filePath = entity.getRootPath()+entity.getPath()+File.separator + entity.getName() + "." + entity.getType();
        //String filePath = "E:\\me\\floor9R10.glb";
        try {
            // 设置响应内容类型为二进制流
            response.setContentType("application/octet-stream");
            // 设置响应头，告诉浏览器返回的数据是一个文件，并指定文件名称
            response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));

            // 输入流，通过输入流读取文件内容
            FileInputStream fileInputStream = new FileInputStream(new File(filePath));

            // 获取响应输出流
            ServletOutputStream outputStream = response.getOutputStream();

            // 将文件内容写入到响应输出流中
            int len;
            byte[] bytes = new byte[1024];
            while ((len = fileInputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, len);
            }

            // 关闭输入流和输出流
            fileInputStream.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 将网络文件转换成文件流
     * @param fileURL 网络文件的URL
     * @param response 响应结果
     */
    public void toFileStream(String fileURL, HttpServletResponse response) {

        try {
            // 创建URL对象
            URL url = new URL(fileURL);

            // 打开连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.connect();

            // 检查响应码
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                // 获取输入流
                InputStream inputStream = connection.getInputStream();

                // 获取响应的输出流
                ServletOutputStream ots = response.getOutputStream();

                // 将文件转为字节数组,再将数组写入响应的输出流
                byte[] buffer = new byte[1024];
                int bytesRead;

                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    ots.write(buffer, 0, bytesRead);
                }

                // 刷新输出流
                ots.flush();

                // 关闭输出流
                ots.close();

                // 关闭输入流
                inputStream.close();
            } else {
                System.out.println("下载文件失败，响应码：" + connection.getResponseCode());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
